package br.com.rhinovet.dao.generico;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import br.com.rhinovet.autenticacao.Conexao;
import br.com.rhinovet.entidade.Usuario;
import br.com.rhinovet.util.StringUtil;

@Repository
@Transactional
public class DaoGenerico<T> implements Serializable {

	private static final Log LOG = LogFactory.getLog(DaoGenerico.class);
	private static final long serialVersionUID = 1L;
	
	@Autowired
	private Conexao conexao;

	public void adicionar(T entidade) throws Exception {
		EntityManager entityManager = conexao.getEntityManager();
		try {
			iniciaTransacao(entityManager);
			entityManager.persist(entidade);
			finalizaTransacao(entityManager);
		} catch (Exception e) {
			LOG.error("Erro ao incluir a Entidade " + entidade.getClass(), 	e.getCause());
			throw new Exception("Erro ao incluir a Entidade ");
		}
	}

	public void editar(T entidade) throws Exception {
		EntityManager entityManager = conexao.getEntityManager();
		try {
			iniciaTransacao(entityManager);
			entityManager.merge(entidade);
			finalizaTransacao(entityManager);
		} catch (Exception e) {
			LOG.error("Erro ao atualizar" + entidade.getClass(), e.getCause());
			throw new Exception("Erro ao atualizar ");
		}
	}

	public void remover(Class<T> clazz, long idEntidade) throws Exception {
		EntityManager entityManager = conexao.getEntityManager();
		Object find = entityManager.find(clazz, idEntidade);

		try {
			iniciaTransacao(entityManager);
			entityManager.remove(find);
			finalizaTransacao(entityManager);
		} catch (Exception e) {
			LOG.error("Erro ao excluir " + clazz.getClass(), e.getCause());
			throw new Exception("Erro ao excluir ");
		}
	}

	@SuppressWarnings({ "unchecked", "hiding" })
	public <T> T obterPorId(Class<T> classe, Long idEntidade) throws Exception {
		try {

			if (idEntidade == null || idEntidade == 0) {
				throw new Exception("Id da entidade não fornecido.");
			}

			StringBuilder sqlEntidade = new StringBuilder();
			sqlEntidade.append(" Select e from " + classe.getSimpleName() + " e ");
			sqlEntidade.append(" where e.id = :idEntidade");

			Query queryEntidade = getSession().createQuery(sqlEntidade.toString());

			queryEntidade.setParameter("idEntidade", idEntidade);
			
			
			getSession().clear();
			return (T) queryEntidade.uniqueResult();

		} catch (Exception e) {
			LOG.error("Erro ao excluir " + classe.getClass(), e.getCause());
			throw new Exception("Não foi possível pesquisar o registro ");
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> buscarTodos(Class<T> classe, String campoOrdenacao, String ordenacaoSecundaria) throws Exception {
		String nomeClasse = classe.getSimpleName();
		try {
			
			StringBuilder sqlCompleto = new StringBuilder();
			
			sqlCompleto.append(" Select e from " + nomeClasse + " e");
			sqlCompleto.append(" order by e." + campoOrdenacao + " ");
			if (StringUtil.textoPreenchido(ordenacaoSecundaria)) {
				sqlCompleto.append(" , e."+ordenacaoSecundaria+" ");
			}
			sqlCompleto.append(" asc ");
			
			Query query = getSession().createQuery(sqlCompleto.toString());
			
			List<T> lista = query.list();

			getSession().clear();
			return lista;
		} catch (Exception e) {
			LOG.error("Não foi possível buscar a lista total. " + classe.getClass(), e.getCause());
			throw new Exception("Não foi possível pesquisar o registro ");
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> buscarPorCampo(Class<T> classe, String campo, Object valor, String campoOrdenacao, String ordenacaoSecundaria) throws Exception {
		try {
			StringBuilder sqlCompleto = new StringBuilder();
			String nomeClasse = classe.getSimpleName();
			
			sqlCompleto.append(" Select e from " + nomeClasse + " e");
			sqlCompleto.append(" where " + campo + " like '%"+valor+"%' ") ;
			sqlCompleto.append(" order by e." + campoOrdenacao + " ");
			if (StringUtil.textoPreenchido(ordenacaoSecundaria)) {
				sqlCompleto.append(" , e."+ordenacaoSecundaria+" ");
			}
			
			sqlCompleto.append(" asc ");
			
			
			Query query = getSession().createQuery(sqlCompleto.toString());
			List<T> lista = query.list();
			
			getSession().clear();
			return lista;
			
		} catch (Exception e) {
			LOG.error("Não foi possível buscar a lista total. " + classe.getClass(), e.getCause());
			throw new Exception("Não foi possível pesquisar o registro ");
		}
	}
	
	public List<T> buscarPorRelacionamento(Class<T> classe, String relacionamento, Long idObjeto) throws Exception {
		try {
			StringBuilder sqlCompleto = new StringBuilder();
			String nomeClasse = classe.getSimpleName();
			
			sqlCompleto.append(" Select e from " + nomeClasse + " e");
			sqlCompleto.append(" where e." + relacionamento + ".id = " + idObjeto) ;
			
			Query query = getSession().createQuery(sqlCompleto.toString());
			@SuppressWarnings("unchecked")
			List<T> lista = query.list();
			
			getSession().clear();
			return lista;
			
		} catch (Exception e) {
			LOG.error("Não foi possível buscar a lista total. " + classe.getClass(), e.getCause());
			throw new Exception("Erro ao buscar lista por relacionamento " + classe.getClass());
		}
		
	}

	public void cadastroEmLote(List<T> listaEntidade) throws Exception {
		EntityManager entityManager = conexao.getEntityManager();
		try {
			iniciaTransacao(entityManager);
			for (T entidade : listaEntidade) {
				entityManager.persist(entidade);
			}
			finalizaTransacao(entityManager);
		} catch (Exception e) {
			LOG.error("Não foi possível buscar a lista total. ", e.getCause());
			throw new Exception("Não foi possível pesquisar o registro ");
		}
	}
	
	public Usuario realizarLogin(String login, String senha) throws Exception {
		try {
			StringBuilder sqlTotal = new StringBuilder();
			sqlTotal.append("select e from Usuario e ");
			sqlTotal.append("where e.login = :loginUsuario ");
			sqlTotal.append("and e.senha = :senhaUsuario ");
			
			Query query = getSession().createQuery(sqlTotal.toString());
			query.setParameter("loginUsuario", login);
			query.setParameter("senhaUsuario", senha);
			
			Usuario usuario = (Usuario) query.uniqueResult();
			
			return usuario;
			
		} catch (Exception e) {
			throw new Exception("Erro ao realizar o login");
		}
	}
	
	public boolean verificarTabelaVazia(Class<T> classe) {
		StringBuilder sqlTotal = new StringBuilder();
		sqlTotal.append(" select count(e.id) from "+classe.getSimpleName()+" e");
		
		Query query = getSession().createQuery(sqlTotal.toString());
		Long quantidade = (Long) query.uniqueResult();
		
		if (quantidade > 0) {
			return false;
		} else {
			return true;
		}
	}

	public Session getSession() {
		Session session = (Session) conexao.getEntityManager().getDelegate();

		if (!session.isOpen()) {
			session = session.getSessionFactory().openSession();
		}

		return session;
	}
	
	public void iniciaTransacao(EntityManager entityManager) {
		entityManager.getTransaction().begin();
	}
	
	public void finalizaTransacao(EntityManager entityManager) {
		entityManager.getTransaction().commit();

	}

}
